home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1992 June: ROMin Holiday / ADC Developer CD (1992-06) (''ROMin Holiday'')_iso / Developer Connection - 06-1992.iso / Developer Essentials / DTS Sample Code / System 7.0 Samples / ProcDoggie 1.0a6 / UProcessUtils.inc1.p < prev    next >
Encoding:
Text File  |  1991-02-07  |  35.2 KB  |  979 lines  |  [TEXT/MPS ]

  1. {-------------------------------------------------------------------------------
  2. #
  3. #    Apple Macintosh Developer Technical Support
  4. #
  5. #    Code for Process Manager utilities
  6. #
  7. #    Program:    ProcDoggie
  8. #    File:        UProcessUtils.inc1.p - Pascal Implementation
  9. #
  10. #    by:        Forrest Tanaka
  11. #
  12. #    Copyright © 1988-1991 Apple Computer, Inc.
  13. #    All rights reserved.
  14. #
  15. -------------------------------------------------------------------------------}
  16. {[j=20/57/1$] Pasmat Options}
  17. {$R-}
  18.  
  19.  
  20.     CONST
  21.         kCaseSens = TRUE; {Pass to EqualString for case-sensitive check}
  22.         kDiacSens = TRUE; {Pass to EqualString for diacritical-sensitive check}
  23.  
  24.  
  25. {$S ProcessUtils}
  26. (*******************************************************************************
  27. * Private: EqualFSSpec - Check equality of FSSpec records
  28. *
  29. * EqualFSSpec returns TRUE if the file specified by spec1 refers to the same
  30. * file as the one specified by spec2.  Otherwise, EqualFSSpec returns FALSE.
  31. *
  32. * To compare names, I’m using EqualString with no case sensitivity, but with
  33. * sensitivity to diacriticals.  This isn’t usually the recommended way of
  34. * comparing strings, because the Script Manager has routines for comparing
  35. * strings in a more sophisticated, localizable way.  But the File Manager uses
  36. * _CmpString, which is the assembly language equivalent of EqualString, so
  37. * that’s the way I must do things here.
  38. *******************************************************************************)
  39.  
  40.     FUNCTION EqualFSSpec (spec1: FSSpec;
  41.                           spec2: FSSpec): Boolean;
  42.  
  43.     BEGIN
  44.         EqualFSSpec := (spec1.vRefNum = spec2.vRefNum) AND
  45.                        (spec1.parID = spec2.parID) AND
  46.                        (EqualString (spec1.name, spec2.name, NOT kCaseSens,
  47.                         kDiacSens))
  48.     END;
  49.  
  50.  
  51. {$S ProcessUtils}
  52. (*******************************************************************************
  53. * Public: CreateDocList
  54. *
  55. * A document list record is allocated and initialized.  Pretty simple.
  56. *******************************************************************************)
  57.  
  58.     FUNCTION CreateDocList (openOrPrint: LaunchModeCode): DocListHnd;
  59.  
  60.         VAR
  61.             newList: DocListHnd; {Handle to the new DocListRec}
  62.  
  63.     BEGIN
  64.         IF (openOrPrint = kOpenLaunch) OR (openOrPrint = kPrintLaunch) THEN
  65.             BEGIN
  66.                 newList := DocListHnd(NewHandle (SIZEOF (DocListRec)));
  67.                 IF newList <> NIL THEN
  68.                     BEGIN
  69.                         newList^^.docList := NIL;
  70.                         newList^^.openPrint := openOrPrint
  71.                     END;
  72.                 CreateDocList := newList
  73.             END
  74.     END;
  75.  
  76.  
  77. {$S ProcessUtils}
  78. (*******************************************************************************
  79. * Public: IsEmptyDocList
  80. *
  81. * If the document list record is NIL, or if the docList field is NIL, then the
  82. * given document list is empty.
  83. *******************************************************************************)
  84.  
  85.     FUNCTION IsEmptyDocList (theList: DocListHnd): Boolean;
  86.  
  87.     BEGIN
  88.         IsEmptyDocList := (theList = NIL) | (theList^^.docList = NIL)
  89.     END;
  90.  
  91.  
  92. {$S ProcessUtils}
  93. (*******************************************************************************
  94. * Public: AddToDocList
  95. *
  96. * A new document list entry is allocated and then "newFile" is copied into its
  97. * docFile field.  It is then added to end of "theList".
  98. *******************************************************************************)
  99.  
  100.     FUNCTION AddToDocList (newFile: FSSpec;
  101.                            theList: DocListHnd): OSErr;
  102.  
  103.         VAR
  104.             newEntry:  DocListEntryHnd; {Handle to the new DocListEntryRec}
  105.             currEntry: DocListEntryHnd; {Handle to DocListEntryRec being checked}
  106.  
  107.     BEGIN
  108.         (* Create a new document list entry *)
  109.         newEntry := DocListEntryHnd (NewHandle (SIZEOF (DocListEntryRec)));
  110.         IF newEntry <> NIL THEN
  111.             BEGIN
  112.                 (* Initialize the new document list entry *)
  113.                 newEntry^^.next := NIL;
  114.                 newEntry^^.docFile := newFile;
  115.  
  116.                 IF IsEmptyDocList (theList) THEN
  117.                     theList^^.docList := newEntry
  118.                 ELSE
  119.                     BEGIN
  120.                         (* Start from the head of the list *)
  121.                         currEntry := theList^^.docList;
  122.  
  123.                         (* Find the end of the list *)
  124.                         WHILE currEntry^^.next <> NIL DO
  125.                             currEntry := currEntry^^.next;
  126.  
  127.                         (* Attach the new entry to the end of the list *)
  128.                         currEntry^^.next := newEntry
  129.                     END;
  130.                 AddToDocList := noErr
  131.             END
  132.         ELSE
  133.             AddToDocList := MemError
  134.     END;
  135.  
  136.  
  137. {$S ProcessUtils}
  138. (*******************************************************************************
  139. * Private: DisposeAllDocEntries - Dispose of all document list entries
  140. *
  141. * Ahhh. . . good old, basic, text-book recursion.  This routine has a very small
  142. * stack frame, so the recursion isn’t very expensive in this case.  Document
  143. * lists are normally very short too.
  144. *
  145. * This routine disposes of every document list entry in a linked list of
  146. * document list entries starting from the entry docListEntry.  If docListEntry
  147. * isn’t NIL and it isn’t a valid DocListEntryHnd, DisposeAllDocEntries probably
  148. * blows up in very bad ways.
  149. *******************************************************************************)
  150.  
  151.     PROCEDURE DisposeAllDocEntries (docListEntry: DocListEntryHnd);
  152.  
  153.     BEGIN
  154.         IF docListEntry <> NIL THEN
  155.             BEGIN
  156.                 IF docListEntry^^.next <> NIL THEN
  157.                     DisposeAllDocEntries (docListEntry^^.next);
  158.                 DisposHandle (Handle(docListEntry))
  159.             END
  160.     END;
  161.  
  162.  
  163. {$S ProcessUtils}
  164. (*******************************************************************************
  165. * Public: DisposeDocList
  166. *
  167. * Should be pretty obvious how this works.
  168. *******************************************************************************)
  169.  
  170.     PROCEDURE DisposeDocList (theList: DocListHnd);
  171.  
  172.     BEGIN
  173.         IF theList <> NIL THEN
  174.             BEGIN
  175.                 (* Dispose of all document list entries *)
  176.                 DisposeAllDocEntries (theList^^.docList);
  177.  
  178.                 (* Dispose of the document list record *)
  179.                 DisposHandle (Handle(theList))
  180.             END
  181.     END;
  182.  
  183.  
  184. {$S ProcessUtils}
  185. (*******************************************************************************
  186. * Public: WereInFront
  187. *
  188. * The current application’s process serial number can always be represented by
  189. * setting the high long integer to 0 and setting the low long integer to the
  190. * constant kCurrentProcess.  This process serial number is compared against the
  191. * process serial number of the front application.  If they’re the same, then
  192. * this application is the front application.
  193. *******************************************************************************)
  194.  
  195.     FUNCTION WereInFront: Boolean;
  196.  
  197.         VAR
  198.             thisProcess:  ProcessSerialNumber; {Current process’s PSN}
  199.             frontProcess: ProcessSerialNumber; {Front process’s PSN}
  200.             inFront:      Boolean;             {TRUE if we’re in front}
  201.             error:        OSErr;
  202.  
  203.         PROCEDURE RecoverError (errorCode: OSErr);
  204.  
  205.         BEGIN
  206.             WereInFront := TRUE;
  207.             EXIT (WereInFront)
  208.         END;
  209.  
  210.     BEGIN
  211.         (* Make a PSN for this application *)
  212.         thisProcess.highLongOfPSN := 0;
  213.         thisProcess.lowLongOfPSN := kCurrentProcess;
  214.  
  215.         (* Get the PSN of the front process *)
  216.         error := GetFrontProcess ((*<*)frontProcess);
  217.         IF error <> noErr THEN
  218.             RecoverError (error);
  219.  
  220.         (* See if this application is the front process *)
  221.         error := SameProcess (thisProcess, frontProcess, (*<*)inFront);
  222.         IF error <> noErr THEN
  223.             RecoverError (error);
  224.  
  225.         WereInFront := inFront
  226.     END;
  227.  
  228.  
  229. {$S ProcessUtils}
  230. (*******************************************************************************
  231. * Private: CreateDocListDesc - Create an AE descriptor for a list of documents
  232. *
  233. * This routine takes a document list in the "docList" parameter and converts it
  234. * to an 'appa' (typeAppParameters) AppleEvent descriptor containing a list of
  235. * documents to be opened by the Process Manager’s LaunchApplication routine.
  236. * This descriptor is returned.
  237. *
  238. * To do this, we have to first build an AppleEvent for an 'odoc'
  239. * (kAEOpenDocuments) or a 'pdoc' (kAEPrintDocuments) event that contains the
  240. * same list of documents that are contained in the "theDocList" parameter.  Once
  241. * that’s done, this AppleEvent is coerced into an typeAppParameters descriptor.
  242. *
  243. * To create the kAEOpenDocuments or kAEPrintDocuments AppleEvent, a target
  244. * address descriptor is needed.  Because we’re converting the AppleEvent into a
  245. * typeAppParameters descriptor rather than sending it somewhere, it doesn’t
  246. * matter what target address we use.  I just used the process serial number
  247. * (PSN) of this application as a dummy value.  Because this is an application
  248. * and not something like a driver, I can just use the "kCurrentProcess" constant
  249. * to represent this application’s PSN.  A descriptor is made of this PSN, then
  250. * this PSN is used when creating the new kAEOpenDocuments AppleEvent.  Once this
  251. * AppleEvent is created, the PSN descriptor is no longer needed and is disposed
  252. * of.
  253. *
  254. * kAEOpenDocuments AppleEvents needs a list of file aliases; each file alias
  255. * represents a document to open.  To do this, a new descriptor list is created,
  256. * and an alias record is created.  Then, each document in the "theDocList"
  257. * parameter is converted to an alias in the alias record, then the alias record
  258. * is manually converted into an 'alis' (typeAlias) descriptor.  I did this
  259. * manually because an alias is already a handle, and it didn’t seem necessary to
  260. * go through the extra expense of the AECreateDesc routine.  This typeAlias
  261. * descriptor is then added to the descriptor list we created earlier in this
  262. * paragraph.  Once the entire document list is converted to the descriptor list,
  263. * the alias record is no longer needed and is disposed of.
  264. *
  265. * This descriptor list full of document aliases is then added to the
  266. * kAEOpenDocuments AppleEvent I created.  Because the descriptor list is copied
  267. * into the AppleEvent, it is disposed of after the AEPutParamDesc call.
  268. *
  269. * Then, the most crucial call is made, AECoerceDesc.  This routine coerces the
  270. * AppleEvent we just made into a typeAppParameters descriptor.  The record
  271. * specified by the dataHandle of this descriptor is exactly in the same format
  272. * as the AppParameters record defined in the “Launching Other Applications”
  273. * section of the Process Manager chapter of Inside Macintosh VI.
  274. *
  275. * You can HLock the dataHandle, then set the "launchAppParameters" field of the
  276. * LaunchParamBlockRec to the dereferenced dataHandle.  LaunchApplication can
  277. * then launch the specified application with the document list that was created.
  278. * If the launched application is high-level event aware (as specified in its
  279. * SIZE resource), the Process Manager converts LaunchAppParameters back to a
  280. * real high-level event which is processed by the application to (hopefully)
  281. * open the documents.  Remember that LaunchAppParameters can be any high-level
  282. * event, but it’ll usually be used to pass an 'odoc' or 'pdoc' event, and that’s
  283. * what we’re using here.  If the launched application isn’t high-level event
  284. * aware, and if LaunchAppParameters is an 'odoc' or 'pdoc' event, then the
  285. * Process Manager will convert the 'odoc' or 'pdoc' event into the old-style
  286. * application parameters so that an old-style application can still open
  287. * documents properly.
  288. *
  289. * This routine is important.  Read it.  Understand it.  Then teach me how it
  290. * works.
  291. *******************************************************************************)
  292.  
  293.     FUNCTION CreateDocListDesc (theDocList:     DocListHnd;
  294.                                 VAR launchDesc: AEDesc): OSErr;
  295.  
  296.         CONST
  297.             kPutAtEnd = 0; {For AEPutDesc; put new descriptor at end of list}
  298.  
  299.         VAR
  300.             applMessage:  AppleEvent;          {odoc/pdoc event w/ spec’d doc list}
  301.             docDescList:  AEDescList;          {Desc list of doc alias descs}
  302.             selfPSN:      ProcessSerialNumber; {Our own PSN; not really used here}
  303.             selfAddress:  AEDesc;              {Desc for our own PSN; ditto}
  304.             docDesc:      AEDesc;              {Desc for doc alias}
  305.             docAlias:     AliasHandle;         {Alias for specified docs}
  306.             currDocEntry: DocListEntryHnd;     {Handle to current doc list entry}
  307.             openPrintCmd: AEEventID;           {odoc or pdoc event?}
  308.             wasChanged:   Boolean;             {TRUE if UpdateAlias changed alias}
  309.             error:        OSErr;
  310.  
  311.         PROCEDURE RecoverError (errorCode: Integer);
  312.  
  313.         BEGIN
  314.             IF selfAddress.dataHandle <> NIL THEN
  315.                 error := AEDisposeDesc ((*◊*)selfAddress);
  316.             IF applMessage.dataHandle <> NIL THEN
  317.                 error := AEDisposeDesc ((*◊*)applMessage);
  318.             IF docDescList.dataHandle <> NIL THEN
  319.                 error := AEDisposeDesc ((*◊*)docDescList);
  320.             IF docAlias <> NIL THEN
  321.                 DisposHandle (Handle(docAlias));
  322.             CreateDocListDesc := errorCode;
  323.             EXIT (CreateDocListDesc)
  324.         END;
  325.  
  326.     BEGIN
  327.         selfAddress.dataHandle := NIL;
  328.         applMessage.dataHandle := NIL;
  329.         docDescList.dataHandle := NIL;
  330.         docAlias := NIL;
  331.  
  332.         (* Make descriptor for my own PSN; just for AECreateAppleEvent’s yuks *)
  333.         selfPSN.highLongOfPSN := 0;
  334.         selfPSN.lowLongOfPSN := kCurrentProcess;
  335.         error := AECreateDesc (typeProcessSerialNumber, @selfPSN,
  336.                 SIZEOF (ProcessSerialNumber), (*<*)selfAddress);
  337.         IF error <> noErr THEN
  338.             RecoverError (error);
  339.  
  340.         (* Create an AppleEvent for our list of document descriptors *)
  341.         IF theDocList^^.openPrint = kOpenLaunch THEN
  342.             openPrintCmd := kAEOpenDocuments
  343.         ELSE IF theDocList^^.openPrint = kPrintLaunch THEN
  344.             openPrintCmd := kAEPrintDocuments;
  345.         error := AECreateAppleEvent (kCoreEventClass, openPrintCmd, selfAddress,
  346.                 kAutoGenerateReturnID, kAnyTransactionID, (*<*)applMessage);
  347.         IF error <> noErr THEN
  348.             RecoverError (error);
  349.  
  350.         (* PSN copied into the odoc or pdoc event, so don’t need it anymore *)
  351.         error := AEDisposeDesc ((*◊*)selfAddress);
  352.  
  353.         (* Create list of descriptors for files; don’t use list factorization *)
  354.         error := AECreateList (NIL, 0, FALSE, (*<*)docDescList);
  355.         IF error <> noErr THEN
  356.             RecoverError (error);
  357.  
  358.         (* Create an alias for the first document *)
  359.         currDocEntry := theDocList^^.docList;
  360.         HLock (Handle(currDocEntry));
  361.         error := NewAlias (NIL, currDocEntry^^.docFile, (*<*)docAlias);
  362.         HUnlock (Handle(currDocEntry));
  363.         IF error <> noErr THEN
  364.             RecoverError (error);
  365.  
  366.         (* Put each document in the document list into document descriptor list *)
  367.         WHILE currDocEntry <> NIL DO
  368.             BEGIN
  369.                 (* Convert alias into an alias descriptor manually *)
  370.                 docDesc.descriptorType := typeAlias;
  371.                 docDesc.dataHandle := Handle(docAlias);
  372.  
  373.                 (* Put the alias descriptor into the document descriptor list *)
  374.                 error := AEPutDesc ((*◊*)docDescList, kPutAtEnd, docDesc);
  375.                 IF error <> noErr THEN
  376.                     RecoverError (error);
  377.  
  378.                 (* Go to the next document in the document list *)
  379.                 currDocEntry := currDocEntry^^.next;
  380.  
  381.                 (* Convert the next document’s FSSpec into the alias *)
  382.                 IF currDocEntry <> NIL THEN
  383.                     BEGIN
  384.                         HLock (Handle(currDocEntry));
  385.                         error := UpdateAlias  (NIL, currDocEntry^^.docFile,
  386.                                 (*<*)docAlias, (*<*)wasChanged);
  387.                         HUnlock (Handle(currDocEntry));
  388.                         IF error <> noErr THEN
  389.                             RecoverError (error)
  390.                     END
  391.             END;
  392.  
  393.         (* All aliases are in docDescList, so don’t need the alias record *)
  394.         DisposHandle (Handle(docAlias));
  395.         docAlias := NIL;
  396.  
  397.         (* Put the descriptor list of documents into the odoc or pdoc event *)
  398.         error := AEPutParamDesc ((*◊*)applMessage, keyDirectObject, docDescList);
  399.         IF error <> noErr THEN
  400.             RecoverError (error);
  401.  
  402.         (* The descriptor list is copied into the odoc event, so get rid of it *)
  403.         error := AEDisposeDesc (docDescList);
  404.  
  405.         (* Convert the odoc event to a descriptor suitable for the launch PB *)
  406.         error := AECoerceDesc (applMessage, typeAppParameters, (*<*)launchDesc);
  407.         IF error <> noErr THEN
  408.             RecoverError (error);
  409.  
  410.         (* We’re really interested in launchDesc, so don’t need odoc message *)
  411.         error := AEDisposeDesc (applMessage);
  412.  
  413.         CreateDocListDesc := noErr
  414.     END;
  415.  
  416.  
  417. {$S ProcessUtils}
  418. (*******************************************************************************
  419. * Private: SendOpenAppEvent - Send an 'oapp' event to an application
  420. *
  421. * When an application that is high-level event aware (as indicated by the SIZE
  422. * resource flag), it won’t open an untitled document at launch until it gets an
  423. * 'oapp' AppleEvent.  This routine sends an 'oapp' event to the application
  424. * specified by the processNum parameter.
  425. *
  426. * First, a descriptor is created that contains the process serial number of the
  427. * target application.  This descriptor is then added to the 'oapp' AppleEvent.
  428. * Finally, this AppleEvent is sent to the target application which should open
  429. * an untitled document in response.
  430. *
  431. * This routine isn’t called if the application is to open specific documents
  432. * when it’s launched.
  433. *
  434. * If an error occurs, the error code is returned.
  435. *******************************************************************************)
  436.  
  437.     FUNCTION SendOpenAppEvent (processNum: ProcessSerialNumber): OSErr;
  438.  
  439.         VAR
  440.             theBaby:   AEAddressDesc; {PSN desc. of process that’s been opened}
  441.             openEvent: AppleEvent;    {'oapp' AppleEvent}
  442.             reply:     AppleEvent;    {Reply from receiving application; ignored}
  443.             error:     OSErr;
  444.  
  445.         PROCEDURE RecoverError (errorCode: Integer);
  446.  
  447.         BEGIN
  448.             IF theBaby.dataHandle <> NIL THEN
  449.                 error := AEDisposeDesc ((*◊*)theBaby);
  450.             IF openEvent.dataHandle <> NIL THEN
  451.                 error := AEDisposeDesc ((*◊*)openEvent);
  452.             IF reply.dataHandle <> NIL THEN
  453.                 error := AEDisposeDesc ((*◊*)reply);
  454.             SendOpenAppEvent := errorCode;
  455.             EXIT (SendOpenAppEvent)
  456.         END;
  457.  
  458.     BEGIN
  459.         theBaby.dataHandle := NIL;
  460.         openEvent.dataHandle := NIL;
  461.         reply.dataHandle := NIL;
  462.  
  463.         (* Create the Process Serial Number event descriptor *)
  464.         error := AECreateDesc (typeProcessSerialNumber, Ptr(@processNum),
  465.                 SIZEOF (ProcessSerialNumber), (*<*)theBaby);
  466.         IF error <> noErr THEN
  467.             RecoverError (error);
  468.  
  469.         (* Create 'oapp' event with the specified process serial number *)
  470.         error := AECreateAppleEvent (kCoreEventClass, kAEOpenApplication,
  471.                 theBaby, kAutoGenerateReturnID, kAnyTransactionID, (*<*)openEvent);
  472.         IF error <> noErr THEN
  473.             RecoverError (error);
  474.  
  475.         (* Send the 'oapp' event *)
  476.         error := AESend (openEvent, (*<*)reply, kAENoReply, kAENormalPriority,
  477.                 0, NIL, NIL);
  478.         IF error <> noErr THEN
  479.             RecoverError (error);
  480.  
  481.         (* Dispose of the descriptor and event *)
  482.         error := AEDisposeDesc ((*◊*)theBaby);
  483.         error := AEDisposeDesc ((*◊*)openEvent);
  484.         IF reply.dataHandle <> NIL THEN
  485.             error := AEDisposeDesc ((*◊*)reply)
  486.     END;
  487.  
  488.  
  489. {$S ProcessUtils}
  490. (*******************************************************************************
  491. * Private: LaunchProcApp - Launch an application
  492. *
  493. * This routine saves a bit of work when launching an application because it sets
  494. * up the launch parameter block and manages the construction of the application
  495. * list descriptor.  The application to open is specified in processFile.  If
  496. * there are any documents for the application to open when it’s launched, then
  497. * the document list is passed in docList.  If there aren’t any documents for the
  498. * application to open, then docList is NIL.  The options parameter specifies the
  499. * options to use when launching.  This contains the values for the LaunchFlags
  500. * data type specified in the Process Manager chapter of Inside Macintosh VI.
  501. *
  502. * The process serial number of the specified process is returned in the
  503. * processNum parameter.  The function result and the launchError parameter both
  504. * return error codes.  One error is returned as a function result.  These kinds
  505. * of errors are generated by any call that occurs during the execution of
  506. * LaunchProcess that is only used to manage the launching of the specified
  507. * application rather than launching the application itself.   The launchError
  508. * parameter returns the error code of any error that occurs when the application
  509. * is actually launched.
  510. *******************************************************************************)
  511.  
  512.     FUNCTION LaunchProcApp (processFile:     FSSpec;
  513.                             docList:         DocListHnd;
  514.                                     options:         LaunchFlags;
  515.                             VAR processNum:  ProcessSerialNumber;
  516.                             VAR launchError: OSErr): OSErr;
  517.  
  518.         VAR
  519.             launchParms: LaunchParamBlockRec; {Parameters for launching a file}
  520.             launchDesc:  AEDesc;              {Document file list descriptor}
  521.             appParms:    AppParametersPtr;    {Pointer to the app parameters}
  522.             error:       OSErr;
  523.  
  524.         PROCEDURE RecoverError (errorCode: OSErr);
  525.  
  526.         BEGIN
  527.             IF launchDesc.dataHandle <> NIL THEN
  528.                 BEGIN
  529.                     HUnlock (Handle(launchDesc.dataHandle));
  530.                     error := AEDisposeDesc (launchDesc)
  531.                 END;
  532.             LaunchProcApp := errorCode;
  533.             EXIT (LaunchProcApp)
  534.         END;
  535.  
  536.     BEGIN
  537.         launchError := noErr;
  538.         launchDesc.dataHandle := NIL;
  539.  
  540.         (* Create the document list descriptor, if there’s a document list *)
  541.         IF docList <> NIL THEN
  542.             BEGIN
  543.                 error := CreateDocListDesc (docList, (*<*)launchDesc);
  544.                 IF error <> noErr THEN
  545.                     RecoverError (error);
  546.                 HLock (Handle(launchDesc.dataHandle));
  547.  
  548.                 (* Descriptor dataHandle in format suitable for LaunchApplication *)
  549.                 appParms := AppParametersPtr(launchDesc.dataHandle^)
  550.             END
  551.         ELSE
  552.             appParms := NIL;
  553.  
  554.         (* Set up the launch parameters *)
  555.         WITH launchParms DO
  556.             BEGIN
  557.                 (*WITH*)launchBlockID := extendedBlock;
  558.                 (*WITH*)launchEPBLength := extendedBlockLen;
  559.                 (*WITH*)launchFileFlags := 0;
  560.                 (*WITH*)launchControlFlags := options;
  561.                 (*WITH*)launchAppSpec := @processFile;
  562.                 (*WITH*)launchAppParameters := appParms
  563.             END;
  564.  
  565.         (* Launch the process *)
  566.         launchError := LaunchApplication (@launchParms);
  567.  
  568.         (* Get rid of the document descriptor list, if there was one *)
  569.         IF docList <> NIL THEN
  570.             BEGIN
  571.                 HUnlock (Handle(launchDesc.dataHandle));
  572.                 error := AEDisposeDesc (launchDesc)
  573.             END
  574.         ELSE
  575.             (* No document descriptor list, so send oapp AppleEvent *)
  576.             IF launchError = noErr THEN
  577.                 error := SendOpenAppEvent (launchParms.launchProcessSN);
  578.  
  579.         (* If the launch was successful, return the PSN of the process *)
  580.         IF launchError = noErr THEN
  581.             processNum := launchParms.launchProcessSN;
  582.  
  583.         LaunchProcApp := noErr
  584.     END;
  585.  
  586.  
  587. {$S ProcessUtils}
  588. (*******************************************************************************
  589. * Private: GetFirstDAName - Get name of first 
  590. *
  591. * GetFirstDAName gets the name of the first desk accessory in the file specified
  592. * by daFile.  This name is returned in the daName parameter.  GetFirstDAName
  593. * returns TRUE if a desk accessory was found in the file.  If no desk accessory
  594. * could be found, then GetFirstDAName returns FALSE and the daName parameter is
  595. * unchanged.
  596. *
  597. * Desk accessories are stored in DRVR resources, but device drivers are also
  598. * stored in DRVR resources.  GetFirstDAName will not return the names of device
  599. * drivers.  How do you tell whether a DRVR resource is a desk accessory or a
  600. * device driver?  Simply check on the first character of the DRVR resource’s
  601. * name.  If it’s a null character, then you’ve got a desk accessory.  If it’s
  602. * any other character, then you’ve got a device driver.
  603. *******************************************************************************)
  604.  
  605.     FUNCTION GetFirstDAName (daFile:     FSSpec;
  606.                              VAR daName: Str255): Boolean;
  607.  
  608.         VAR
  609.             currRF:   Integer; {Ref num of current resource file}
  610.             daRF:     Integer; {Ref num of DA’s resource file}
  611.             drvrRsrc: Handle;  {Handle to a DRVR resource}
  612.             drvrID:   Integer; {ID number of drvrRsrc}
  613.             drvrType: ResType; {Type of resource}
  614.             drvrName: Str255;  {Name of resource}
  615.             numDRVRs: Integer; {Number of DRVR resources in the file}
  616.             index:    Integer; {Index into DRVR resources}
  617.             daFound:  Boolean; {TRUE if a DA was found in daFile}
  618.  
  619.     BEGIN
  620.         daFound := FALSE;
  621.  
  622.         (* Save the current resource file ref num so we can restore it later *)
  623.         currRF := CurResFile;
  624.  
  625.         (* Open the specified resource file *)
  626.         daRF := FSpOpenResFile (daFile, fsRdPerm);
  627.         IF daRF <> -1 THEN
  628.             BEGIN
  629.                 (* Ready to check each DRVR; only checking, so set ResLoad false *)
  630.                 SetResLoad (FALSE);
  631.                 index := 1;
  632.                 numDRVRs := Count1Resources ('DRVR');
  633.  
  634.                 (* Check each DRVR until all checked or a DA was found *)
  635.                 WHILE (index <= numDRVRs) AND (NOT daFound) DO
  636.                     BEGIN
  637.                         drvrRsrc := Get1IndResource ('DRVR', index);
  638.  
  639.                         (* Get information about the resource *)
  640.                         IF drvrRsrc <> NIL THEN
  641.                             BEGIN
  642.                                 GetResInfo (drvrRsrc, (*<*)drvrID, (*<*)drvrType,
  643.                                         (*<*)drvrName);
  644.  
  645.                                 (* Find out whether it’s a DA or not *)
  646.                                 IF (drvrName [0] > CHR (0)) & (drvrName [1] = CHR (0))
  647.                                         THEN
  648.                                     daFound := TRUE
  649.                                 ELSE
  650.                                     index := SUCC (index)
  651.                             END
  652.                     END;
  653.  
  654.                 (* Restore the resource file and reset ResLoad *)
  655.                 SetResLoad (TRUE);
  656.                 CloseResFile (daRF)
  657.             END;
  658.  
  659.         (* Get name of first DA in the file, or nil string if none found *)
  660.         IF daFound THEN
  661.             daName := drvrName
  662.         ELSE
  663.             daName [0] := CHR (0);
  664.         GetFirstDAName := daFound;
  665.         UseResFile (currRF)
  666.     END;
  667.  
  668.  
  669. {$S ProcessUtils}
  670. (*******************************************************************************
  671. * Public: FindProcess
  672. *
  673. * The critical routine called by FindProcess is the GetProcessInfo routine.  The
  674. * process information it returns is checked against "testFile", and against
  675. * "daName" if a desk accessory is being checked.  If they’re equal, then
  676. * FindProcess returns TRUE and puts the process information in "testFileInfo".
  677. * If the entire Process Manager process list is searched without finding a
  678. * match, then FALSE is returned.
  679. *******************************************************************************)
  680.  
  681.     FUNCTION FindProcess (testFile:         FSSpec;
  682.                           daName:           StringPtr;
  683.                           VAR testFileInfo: ProcessInfoRec): Boolean;
  684.  
  685.         VAR
  686.             procInfo:  ProcessInfoRec;      {Information on process being checked}
  687.             procNum:   ProcessSerialNumber; {Serial num of process being checked}
  688.             procName:  Str31;               {Name of open process}
  689.             procSpec:  FSSpec;              {File spec of open process’s file}
  690.             procFound: Boolean;             {TRUE if matching DA was found}
  691.             error:     OSErr;
  692.  
  693.     BEGIN
  694.         (* Start checking from first process in Process Manager’s list *)
  695.         procNum.highLongOfPSN := 0;
  696.         procNum.lowLongOfPSN := kNoProcess;
  697.  
  698.         (* Loop through entire list of open processes or until match *)
  699.         procFound := FALSE;
  700.         WHILE (NOT procFound) & (GetNextProcess ((*◊*)procNum) = noErr) DO
  701.             BEGIN
  702.                 (* Get information about an open process *)
  703.                 procInfo.processInfoLength := SIZEOF (ProcessInfoRec);
  704.                 procInfo.processName := @procName;
  705.                 procInfo.processAppSpec := @procSpec;
  706.                 error := GetProcessInformation (procNum, (*◊*)procInfo);
  707.  
  708.                 (* Is it the same file as the one we’re testing? *)
  709.                 IF error <> noErr THEN
  710.                     IF EqualFSSpec (procInfo.processAppSpec^, testFile) THEN
  711.                         (* Yes; if it’s an application, we’ve found matching app *)
  712.                         IF BAND (procInfo.processMode, modeDeskAccessory) = 0 THEN
  713.                             procFound := TRUE
  714.                         ELSE
  715.                             (* Is it the same DA as the one we’re testing?
  716.                             IF EqualString (procInfo.processName^, daName^,
  717.                                     NOT kCaseSens, kDiacSens) THEN
  718.                                 (* Yes, we’ve found the matching DA *)
  719.                                 procFound := TRUE
  720.             END;
  721.  
  722.         IF procFound THEN
  723.             testFileInfo := procInfo;
  724.         FindProcess := procFound
  725.     END;
  726.  
  727.  
  728. {$S ProcessUtils}
  729. (*******************************************************************************
  730. * Private: LaunchProcDA - Launch a desk accessory
  731. *
  732. * This routine launches the desk accessory that’s in the file specified by
  733. * process file and with the name specified by daName.  If daName is NIL, then
  734. * the first desk accessory in the file, according to Get1IndResource, is
  735. * launched.
  736. *
  737. * As of 7.0b1, LaunchDeskAccessory doesn’t quite work as advertised in Inside
  738. * Macintosh VI if the desk accessory that it’s launching is already open.
  739. * Inside Macintosh VI says that the open desk accessory that’s being launched is
  740. * simply brought to the front.  What I’ve found is that LaunchDeskAccessory
  741. * doesn’t do anything but return opWrErr.  If the file that the desk accessory
  742. * is in is locked, then a new copy of the desk accessory is opened.  I didn’t
  743. * quite like either of those actions, so I explicitly check to see whether the
  744. * desk accessory that I’m about to launch is already open.  If it is, then I
  745. * call SetFrontProcess myself to bring it to the front.
  746. *
  747. * To check for this case, I first make sure that I have a name for the desk
  748. * accessory.  LaunchDeskAccessory accepts NIL as a desk accessory name if the
  749. * caller wants to launch the first desk accessory that the Process Manager finds
  750. * in the specified file.  My routine, LaunchProcess, also allows this.  But I
  751. * don’t know what the Process Manager thinks is the first desk accessory in the
  752. * file.  Since I’m comparing the characteristics of the desk accessory I’m about
  753. * to open against the list of open processes, and since I don’t know which desk
  754. * accessory is about to be launched if I leave the choice up to the Process
  755. * Manager, I just go and get the first desk accessory in the specified file
  756. * myself by calling my routine, GetFirstDAName.  Now that I’m guaranteed to have
  757. * a name for the desired desk accessory, I don’t have to second-guess the
  758. * Process Manager’s choice for the “first” desk accessory in the specified file.
  759. *
  760. * You can’t tell LaunchDeskAccessory that you want to terminate after the desk
  761. * accessory is launched, so I terminate myself by calling ExitToShell if the
  762. * terminate parameter was set to TRUE.  You also can’t tell LaunchDeskAccessory
  763. * that you want the desk accessory launched into the background.  I still
  764. * haven’t thought of a clean way to do this, so for now, I don’t offer that as
  765. * an option.  I can think of a few dirty ways.
  766. *******************************************************************************)
  767.  
  768.     FUNCTION LaunchProcDA (processFile:     FSSpec;
  769.                            daName:          StringPtr;
  770.                            options:         LaunchFlags;
  771.                            VAR processNum:  ProcessSerialNumber;
  772.                            VAR launchError: OSErr): OSErr;
  773.  
  774.         VAR
  775.             procInfo:    ProcessInfoRec;      {Used to search open processes}
  776.             procNum:     ProcessSerialNumber; {Process serial num of open process}
  777.             firstDAName: Str255;              {File’s 1st DA name if none spec’d}
  778.             daOpen:      Boolean;             {TRUE if spec’d DA is already open}
  779.             error:       OSErr;
  780.  
  781.     BEGIN
  782.         error := noErr;
  783.         launchError := noErr;
  784.  
  785.         (* If no DA name specified, get name of first DA in processFile *)
  786.         IF daName = NIL THEN
  787.             IF GetFirstDAName (processFile, (*<*)firstDAName) THEN
  788.                 IF firstDAName [0] > CHR (0) THEN
  789.                     daName := @firstDAName
  790.                 ELSE
  791.                     daName := NIL;
  792.  
  793.         (* If got name for new DA, see if DA process open w/ same name and file *)
  794.         IF daName <> NIL THEN
  795.             daOpen := FindProcess (processFile, daName, (*<*)procInfo)
  796.         ELSE
  797.             daOpen := FALSE;
  798.  
  799.         (* Launch the DA to the front, or set it to front if already launched *)
  800.         IF daOpen THEN
  801.             error := SetFrontProcess (procInfo.processNumber)
  802.         ELSE
  803.             launchError := LaunchDeskAccessory (@processFile, daName);
  804.  
  805.         (* If the terminate flag is set, glad to oblige *)
  806.         IF BAND (options, launchContinue) = 0 THEN
  807.             ExitToShell;
  808.  
  809.         (* Return the process serial number of the DA *)
  810.         IF (error = noErr) AND (launchError = noErr) THEN
  811.             BEGIN
  812.                 daOpen := FindProcess (processFile, daName, (*<*)procInfo);
  813.                 IF daOpen THEN
  814.                     processNum := procInfo.processNumber
  815.                 ELSE
  816.                     BEGIN
  817.                         procInfo.processNumber.highLongOfPSN := 0;
  818.                         procInfo.processNumber.lowLongOfPSN := kNoProcess;
  819.                         processNum := procInfo.processNumber
  820.                     END
  821.             END;
  822.  
  823.         LaunchProcDA := error
  824.     END;
  825.  
  826.  
  827. {$S ProcessUtils}
  828. (*******************************************************************************
  829. * Public: LaunchProcess
  830. *
  831. * The terminate and foreground parmeters are used to set up the flags for
  832. * launching in the launchOptions variable.  The launch parameter block is set up
  833. * with the specified file and launch flags.
  834. *
  835. * If the launchContinue flags is clear, then the LaunchApplication function
  836. * doesn’t return even if it fails.
  837. *******************************************************************************)
  838.  
  839.     FUNCTION LaunchProcess (processFile:     FSSpec;
  840.                             daName:          StringPtr;
  841.                             docList:         DocListHnd;
  842.                                     options:         LaunchFlags;
  843.                             VAR returnPSN:   ProcessSerialNumber;
  844.                                     VAR launchError: OSErr): OSErr;
  845.  
  846.         VAR
  847.             fileInfo: FInfo;               {File information for file to launch}
  848.             newPSN:   ProcessSerialNumber; {PSN of launched application}
  849.             error:    OSErr;
  850.  
  851.         PROCEDURE RecoverError (errorCode: OSErr);
  852.  
  853.         BEGIN
  854.             LaunchProcess := errorCode;
  855.             EXIT (LaunchProcess)
  856.         END;
  857.  
  858.     BEGIN
  859.         launchError := noErr;
  860.  
  861.         (* Find out whether file to launch is APPL or something else *)
  862.         error := FSpGetFInfo (processFile, (*<*)fileInfo);
  863.         IF error <> noErr THEN
  864.             RecoverError (error);
  865.  
  866.         (* Launch the process *)
  867.         IF fileInfo.fdType = 'APPL' THEN
  868.             BEGIN
  869.                 (* File to be launched is an application; launch it *)
  870.                 error := LaunchProcApp (processFile, docList, options, (*<*)newPSN,
  871.                         (*<*)launchError);
  872.                 IF error <> noErr THEN
  873.                     RecoverError (error);
  874.             END
  875.         ELSE
  876.             BEGIN
  877.                 (* File to be launch is a desk accessory; launch it *)
  878.                 error := LaunchProcDA (processFile, daName, options, (*<*)newPSN,
  879.                         (*<*)launchError);
  880.                 IF error <> noErr THEN
  881.                     RecoverError (error)
  882.             END;
  883.  
  884.         (* Return the PSN of the new process *)
  885.         IF launchError = noErr THEN
  886.             returnPSN := newPSN;
  887.  
  888.         LaunchProcess := noErr
  889.     END;
  890.  
  891.  
  892. {$S ProcessUtils}
  893. (*******************************************************************************
  894. * Public: CountProcesses
  895. *
  896. * It’s a pretty simple and straightforward algorithm.
  897. *******************************************************************************)
  898.  
  899.     FUNCTION CountProcesses: Integer;
  900.  
  901.         VAR
  902.             procNum:   ProcessSerialNumber; {Serial num of process being checked}
  903.             procCount: Integer;             {Number of processes found}
  904.  
  905.     BEGIN
  906.         (* Start checking from first process in Process Manager’s list *)
  907.         procNum.highLongOfPSN := 0;
  908.         procNum.lowLongOfPSN := kNoProcess;
  909.  
  910.         (* Loop through entire list of open processes *)
  911.         procCount := 0;
  912.         WHILE GetNextProcess ((*◊*)procNum) = noErr DO
  913.             procCount := SUCC (procCount);
  914.  
  915.         (* Return the number of processes found *)
  916.         CountProcesses := procCount
  917.     END;
  918.  
  919.  
  920. {$S ProcessUtils}
  921. (*******************************************************************************
  922. * Public: TerminateProcess
  923. *
  924. * Terminating a process is done by sending a 'quit' AppleEvent to the process
  925. * whose process serial number is given by "theProcessNum".
  926. *******************************************************************************)
  927.  
  928.     FUNCTION TerminateProcess (theProcessNum: ProcessSerialNumber): OSErr;
  929.  
  930.         VAR
  931.             theDoomed: AEAddressDesc; {PSN descriptor of process to be terminated}
  932.             quitEvent: AppleEvent;    {'quit' AppleEvent}
  933.             reply:     AppleEvent;    {Reply from receiving application; ignored}
  934.             error:     OSErr;
  935.  
  936.         PROCEDURE RecoverError (error: Integer);
  937.  
  938.             VAR
  939.                 result: OSErr;
  940.  
  941.         BEGIN
  942.             IF theDoomed.dataHandle <> NIL THEN
  943.                 result := AEDisposeDesc ((*◊*)theDoomed);
  944.             IF quitEvent.dataHandle <> NIL THEN
  945.                 result := AEDisposeDesc ((*◊*)quitEvent);
  946.             TerminateProcess := error;
  947.             EXIT (TerminateProcess)
  948.         END;
  949.  
  950.     BEGIN
  951.         theDoomed.dataHandle := NIL;
  952.         quitEvent.dataHandle := NIL;
  953.         reply.dataHandle := NIL;
  954.  
  955.         (* Create the Process Serial Number event descriptor *)
  956.         error := AECreateDesc (typeProcessSerialNumber, Ptr(@theProcessNum),
  957.                 SIZEOF (theProcessNum), (*<*)theDoomed);
  958.         IF error <> noErr THEN
  959.             RecoverError (error);
  960.  
  961.         (* Create 'quit' event with the specified process serial number *)
  962.         error := AECreateAppleEvent (kCoreEventClass, kAEQuitApplication,
  963.                 theDoomed, kAutoGenerateReturnID, kAnyTransactionID, (*<*)quitEvent);
  964.         IF error <> noErr THEN
  965.             RecoverError (error);
  966.  
  967.         (* Send the 'quit' event *)
  968.         error := AESend (quitEvent, (*<*)reply, kAENoReply,
  969.                 kAENormalPriority, kNoTimeOut, NIL, NIL);
  970.         IF error <> noErr THEN
  971.             RecoverError (error);
  972.  
  973.         (* PSN in the AppleEvent, so can dispose of PSN descriptor *)
  974.         error := AEDisposeDesc ((*◊*)theDoomed);
  975.  
  976.         (* Dispose of the 'quit' AppleEvent *)
  977.         error := AEDisposeDesc ((*◊*)quitEvent)
  978.     END;
  979.